package com.bungarus;

import com.bungarus.busi.BaseClient;
import com.bungarus.busi.TransferInnerClient;
import com.bungarus.busi.TransferRemedy;
import com.bungarus.busi.TransferServerChildHandlerInitializer;
import com.bungarus.cluster.ClusterManager;
import com.bungarus.cluster.Node;
import com.bungarus.cluster.NodeInfo;
import com.bungarus.config.BungarusSpringConfig;
import com.bungarus.model.AbstractBungarusServer;
import com.bungarus.model.BungarusClientException;
import com.bungarus.model.BungarusServerException;
import io.netty.channel.ChannelHandler;
import org.apache.log4j.Logger;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.data.mongodb.core.MongoTemplate;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

/**
 * Created by tang on 2018/11/24.
 */
public class Transfer extends AbstractBungarusServer {
    Logger logger = Logger.getLogger(Transfer.class);

    private static final int PORT_7600 = 7600;

    public static final String TAG = "transfer";
    /**
     * tag used to name the connecting cluster
     */
    public static final String CLUSTER_TAG_BUSI = "busi";
    /**
     * tags used to name the connecting clusters
     */
    private static final String[] CLUSTER_TAGS = {CLUSTER_TAG_BUSI};
    /**
     * all clients which connect to the nodes in busi cluster
     */
    private List<BaseClient> innerClients = Collections.synchronizedList(new ArrayList<>());

    private static final ApplicationContext springContext =
            new AnnotationConfigApplicationContext(BungarusSpringConfig.class);

    private final ClusterManager clusterManager = new ClusterManager(TAG);

    public static void main(String[] args) throws Exception {
        int[] messagePorts = {PORT_7600};

        Transfer transfer = new Transfer();
        transfer.bind(messagePorts, null);
        transfer.initClusterAndConnectRemoteNode();
        transfer.initRemedy();
        transfer.listen(null);
    }

    public List<BaseClient> getTransferInnerClients() {
        return innerClients;
    }

    @Override
    public void initClusterAndConnectRemoteNode() throws BungarusServerException {
        try {
            //register myself into a cluster
            register2Cluster();
            //connect to remotes servers (xx layer, yy layer) as client
            boolean again;
            do {
                getNodesInCluster();
                again = remoteServerNodes.size() != getRemoteNodeTags().length;
                if(again) {
                    logger.info("FAILED TO GET CLUSTER NODES!!! Try again...");
                }
                else {
                    for(String CLUSTER_TAG : getRemoteNodeTags()) {
                        if(remoteServerNodes.get(CLUSTER_TAG).size() == 0){
                            logger.info("NO NODES FOUND IN CLUSTER " + CLUSTER_TAG + ", Try again...");
                            again = true;
                            break;
                        }
                    }
                }
                if(again) {
                    Thread.sleep(2000);
                }
            } while (again);

            for (String CLUSTER_TAG : getRemoteNodeTags()) {
                CopyOnWriteArrayList<NodeInfo> queue = remoteServerNodes.get(CLUSTER_TAG);
                queue.forEach(nodeInfo -> {
                    try {
                        innerClients.add(connectBusi(nodeInfo));
                    }
                    catch (BungarusClientException e) {
                        e.printStackTrace();
                    }
                });

            }
            this.setStatus(Node.STATUS_RUNNING);
        }
        catch (Exception e) {
            throw new BungarusServerException(e);
        }

    }

    private BaseClient connectBusi(NodeInfo node) throws BungarusClientException {
        TransferInnerClient client = new TransferInnerClient(node, getMongoTemplate4FailedRetry());
        client.run();
        return client;
    }

    @Override
    public void initRemedy() throws Exception {
        TransferRemedy.start(this);
    }

    @Override
    public ClusterManager getClusterManager() {
        return clusterManager;
    }

    @Override
    public ChannelHandler getChildHandlerInitializer() {
        return new TransferServerChildHandlerInitializer(this);
    }

    @Override
    protected MongoTemplate getMongoTemplate4FailedRetry() {
        return (MongoTemplate) Transfer.springContext.getBean("mongoTemplate");
    }

    @Override
    public String getTag() {
        return TAG;
    }

    @Override
    public String tagPortString4Connection() {
        return "(access|7600)";
    }

    @Override
    public String[] getRemoteNodeTags() {
        return CLUSTER_TAGS;
    }
}
